<?php

namespace Biz\service;

use Biz\model\activity\PinTuanSku;
use Biz\model\trade\GroupJoin;
use Biz\model\trade\GroupJoinMember;
use Biz\model\trade\Order as mOrder;

use Biz\model\trade\Order;
use Biz\model\trade\OrderGoods;
use Biz\module\Packages\Recommend\RecommendGoods;
use Biz\model\activity\PinTuan;
use Biz\pockets\constInterface\TeamRotation;
use Biz\service\activity\PinTuan as sPinTuan;
use Biz\pockets\constInterface\ActivityInfo;
use Biz\tools\OrderNo;
use Doraemon\service\User;
use Doraemon\tools\Log as tLog;
use Biz\pockets\serviceTrait\_Async;
use Biz\model\activity\NewCommerExclusive as mNewCommerExclusive;
use Biz\module\Classes\OrderPinTuan;
use Biz\service\TradeMsg as sTradeMsg;
/**
 * @deprecated
 */
class Group implements TeamRotation
{
    use _Async;

    const CREATE_MAX = 5;
    const JOIN_MAX = 5;

    //1000立即参团 1001邀请好友参团 1002查看订单详情 1003发起拼团 1004看看其他拼团商品
    const OPTION_JOIN = 1000;
    const OPTION_SHARE = 1001;
    const OPTION_ORDER_DETAIL = 1002;
    const OPTION_GOODS_DETAIL = 1003;
    const OPTION_GOODS_LIST = 1004;
    const OPTION_R_GOODS_DETAIL = 1013;

    const OPTION_DESC_MAP = [
        self::OPTION_JOIN => '立即参团',
        self::OPTION_SHARE => '邀请好友参团',
        self::OPTION_ORDER_DETAIL => '查看订单详情',
        self::OPTION_GOODS_DETAIL => '我要发起拼团',
        self::OPTION_GOODS_LIST => '看看其他拼团商品',
        self::OPTION_R_GOODS_DETAIL => '重新发起拼团',
    ];

    const OPTION_JOIN_ALLOWED = 1;//可以参团
    const OPTION_JOIN_FORBIDDEN = 2;//不能参团
    const OPTION_JOIN_CREATE = 3;//可以开团

    const TIPS_TYPE_TEXT = 1;//显示文案
    const TIPS_TYPE_TIME = 2;//显示倒计时

    public static function format_team_info($pinTuanData)
    {
        $goods_id = $pinTuanData['goods_id'];
        $skus = $pinTuanData['skus'];
        $skuIds = array_column($skus, 'sku_id');
        $skuInfos = Sku::Sku([$goods_id], Sku::FIELDS_GROUP_ALL, ['activity_type' => ActivityInfo::ACTIVITY_TYPE_PINTUAN]);
        $stock_item = isset($skuInfos[$goods_id]) ? $skuInfos[$goods_id] : [];
        $actual_stock_total = isset($stock_item['base']['goods_stock']) ? $stock_item['base']['goods_stock'] : [];

        $minStock = [];
        $sku_stock = [];
        $price = [];
        foreach ($skus as $sku) {
            $sku_id = $sku['sku_id'];
            $stock = isset($actual_stock_total[$sku_id]) ? $actual_stock_total[$sku_id] : 0;
            $minStock[] = min($sku['actual_stock'], $stock);
            $sku_stock[] = [
                'sku_id' => $sku_id,
                'activity_stock' => $sku['actual_stock'],
                'stock' => $stock,
            ];


            foreach ($skuInfos[$goods_id]['sku_info'] as $skuInfo) {
                if(in_array($skuInfo['sku_id'], $skuIds)) {
                    $price[] = $skuInfo['ori_price'];
                }
            }

            $ori_min_price = min($price);
            $ori_max_price = max($price);
        }

        $group_member_num = $pinTuanData['group_member_num'];
        $activity_status = array_sum($minStock) >= $group_member_num ? $pinTuanData['status'] : PinTuan::STATUS_DELETE;
        $activity_type = ActivityInfo::ACTIVITY_TYPE_PINTUAN;
        $activity_id = $pinTuanData['id'];
        $activity_icon = $pinTuanData['cover_img_url'];
        $type = $pinTuanData['type'];
        $type_desc = PinTuan::TYPE_DESC[$pinTuanData['type']];
        $min_price = $pinTuanData['min_price'];
        $max_price = $pinTuanData['max_price'];
        $activity_stock = $pinTuanData['actual_stock'];
        $begin_time = strtotime($pinTuanData['start_time']);
        $end_time = strtotime($pinTuanData['end_time']);
        $time_down = [
            'sys_time' => time(),
            'begin_time' => $begin_time,
            'end_time' => $end_time,
        ];
        return compact(
            'goods_id',
            'sku_stock',
            'group_member_num',
            'activity_status',
            'activity_type',
            'activity_id',
            'activity_icon',
            'type',
            'type_desc',
            'ori_min_price',
            'ori_max_price',
            'min_price',
            'max_price',
            'activity_stock',
            'time_down'
        );
    }

    /**
     * 开团信息
     * @param mixed $goods_id 商品id
     * @return array|null
     */
    public static function team_info($goods_id)
    {
        if (!isset($goods_id)) return null;
        $goods_ids = is_array($goods_id) ? $goods_id : [$goods_id];
        $result = sPinTuan::getCurrentList($goods_ids);
        $response = [];
        foreach ($result as $k => $rs) {
            $response[$k] = self::format_team_info($rs);
        }
       return  is_array($goods_id) ? $response : (isset($response[$goods_id]) ? $response[$goods_id] : null);
    }

    /**
     * @desc 参团信息
     * @param $team_join_id
     * @param $uid
     * @return array
     * @throws \Exception
     */
    public static function team_join_info($team_join_id, $uid)
    {
        $single = !is_array($team_join_id);

        //查询参团信息
        $groupJoinInfo = self::getGroupJoinInfo($team_join_id);
        if (!$groupJoinInfo) {
            throw new \Exception('参与的团购信息不存在');
        }
        $groupJoinInfos = $single ? [$groupJoinInfo] : $groupJoinInfo;
        $response = [];
        foreach ($groupJoinInfos as $groupJoinInfo) {
            $team_join_status = $groupJoinInfo['status'];//1:进行中 2:拼团成功 3:拼团失败
            $team_join_status_desc = GroupJoin::STATUS_DESC_MAP[$team_join_status];
            $last_member_num = $groupJoinInfo['last_member_num'];
            $fail_reason = $groupJoinInfo['reason'];//2 超时未凑够人数 1 真实库存已为0（已到达最大成团数）
            $fail_reason_desc = isset(GroupJoin::FAIL_REASON_MAP[$fail_reason]) ? GroupJoin::FAIL_REASON_MAP[$fail_reason] : '';
            $team_join_id = $group_join_id = $groupJoinInfo['id'];
            $goods_id = $groupJoinInfo['goods_id'];
            $group_member_num = $groupJoinInfo['group_member_num'];
            $group_id = $groupJoinInfo['group_id'];
            $type = $groupJoinInfo['type'];
            $time_down = $groupJoinInfo['time_down'];


            $is_join = false;//是否参团 bool
            //查询参与成员信息
            $joinMember = GroupJoinMember::getJoinMember($group_join_id, [GroupJoin::STATUS_SUCCESS, GroupJoin::STATUS_FAIL, GroupJoin::STATUS_FAIL_OUT_JOIN]);
            $users = [];
            $selfMember = [];
            foreach ($joinMember as $member) {
                if ($uid > 0 && ($member['uid'] == $uid)) {
                    $selfMember = $member;
                    $is_join = $member['status'] == GroupJoin::STATUS_FAIL_OUT_JOIN  ? false : true;
                }
                if ($member['status'] != GroupJoin::STATUS_FAIL_OUT_JOIN) {
                    if ($member['uid'] == 0) { //机器人
                        $userInfo = json_decode($member['ext'], true);
                    } else {
                        $userInfo = User::getUserInfo($member['uid']);
                    }
                    $users[] = [
                        'nick' => isset($userInfo['nick']) ? self::hideNick($userInfo['nick']) : '',
                        'avatar' => isset($userInfo['avatar']) ? $userInfo['avatar'] : '',
                        'team_leader' => $member['role'],
                    ];
                }
            }
            $group_leader = isset($selfMember['role']) ? $selfMember['role'] : GroupJoinMember::ROLE_PARTNER;//1团长 2参与者
            $team_role = $group_leader;
            $order_no = isset($selfMember['order_no']) ? $selfMember['order_no'] : '';
            $sku_id = isset($selfMember['sku_id']) ? $selfMember['sku_id'] : '';

            $group_alive = self::getPinTuanIdByGoodsId($goods_id) > 0;
            $option_join = self::OPTION_JOIN_FORBIDDEN;//不能参团
            $option_error = '';
            $tips_type = self::TIPS_TYPE_TEXT;
            switch ($team_join_status) {
                case GroupJoin::STATUS_ING://进行中
                    $tips_type = self::TIPS_TYPE_TIME;
                    if ($is_join) {
                        $option_code = self::OPTION_SHARE;//分享
                    } else {
                        $option_code = self::OPTION_JOIN;//参团

                        //查询用户参与团购的场次
                        $join_count = GroupJoinMember::getJoinCount($uid);
                        if ($join_count >= self::JOIN_MAX) {
                            $option_error = vsprintf('抱歉，最多只可以参加%d场团购哦', [self::JOIN_MAX]);
                        } else {
                            if ($type == PinTuan::TYPE_NEW) {
                                //新用户判断 没有支付过 若拼团失败不算支付过（任意渠道、排除节选小程序），且注册时间在12个小时以内（点击拼团时间为准）
                                $newUser = self::checkNewPinTuanUser($uid);
                                //当前用户开团数
                                $create_count = GroupJoin::getCreateCount($uid);
                                if ($create_count < self::CREATE_MAX) {
                                    if ($newUser) {
                                        //拉新团限制:用户与团购活动唯一性校验
                                        $unique = GroupJoinMember::getJoinCount($uid, $group_id);
                                        if ($unique > 0) {
                                            $option_error = '只能参与一次新人团，你可以直接开团哦';
                                            $option_join = self::OPTION_JOIN_CREATE;
                                        } else {
                                            $option_join = self::OPTION_JOIN_ALLOWED;
                                        }
                                    } else {
                                        $option_error = '抱歉，仅限新注册用户参团，你可以直接开团哦';
                                        $option_join = self::OPTION_JOIN_CREATE;
                                    }
                                } else {
                                    $option_error = '抱歉，已到最大开团数哦';
                                }
                            } else {
                                $option_join = self::OPTION_JOIN_ALLOWED;
                            }
                        }
                    }

                    $tips = $is_join ? '还差%s人拼团成功' : '仅剩%s个机会';
                    $tips = vsprintf($tips, [$last_member_num]);
                    break;
                case GroupJoin::STATUS_SUCCESS://拼团成功
                    if ($is_join) {
                        $option_code = self::OPTION_ORDER_DETAIL;//订单详情
                    } else {
                        $option_code = $group_alive ? self::OPTION_GOODS_DETAIL : self::OPTION_GOODS_LIST;//活动结束=>列表 活动有效=>商品
                    }
                    $tips = $is_join ? '恭喜，拼团成功' : '来晚啦，别人已拼团成功';
                    break;
                case GroupJoin::STATUS_FAIL://拼团失败
                    if ($group_alive) {
                        $option_code = $group_leader == GroupJoinMember::ROLE_LEADER ? self::OPTION_R_GOODS_DETAIL : self::OPTION_GOODS_DETAIL;//开团
                    } else {
                        $option_code = self::OPTION_GOODS_LIST;//活动结束=>列表
                    }

                    $tips = $fail_reason == GroupJoin::FAIL_REASON_NOT_ENOUGH_STOCK ? '很遗憾，拼的有点慢，商品已被抢光' : '很遗憾，超时未凑齐好友';
                    break;
                default:
                    throw new \Exception('未定义的团购类型');
                    break;
            }
            $option_desc = self::OPTION_DESC_MAP[$option_code];
            $option_code = $option_code == self::OPTION_R_GOODS_DETAIL ? self::OPTION_GOODS_DETAIL : $option_code;
            $tips = isset($selfMember['status']) && $selfMember['status'] == GroupJoin::STATUS_FAIL_OUT_JOIN ? '来晚啦，别人已拼团成功' : $tips;
            $response[$group_join_id] = compact(
                'team_join_id',
                'group_member_num',
                'last_member_num',
                'order_no',
                'sku_id',
                'team_role',
                'team_join_status',
                'team_join_status_desc',
                'fail_reason_desc',
                'tips_type',
                'tips',
                'option_code',
                'option_desc',
                'option_join',
                'option_error',
                'users',
                'time_down'
            );
        }
        return $single ? $response[$team_join_id] : $response;
    }


    public static function rotationList($goodsId)
    {
        $rotationList = self::ROTATION_LIST;

        //查询正在进行的团购活动id
        $groupId = self::getPinTuanIdByGoodsId($goodsId);
        $infos = GroupJoin::select("*", [
            "group_id" => $groupId,
            "status" => GroupJoin::STATUS_SUCCESS
        ]);
        if ($infos) foreach ($infos as $value) {
            $userInfo = User::getUserInfo($value['uid']);
            $rotationList[] = (isset($userInfo['nick']) && $userInfo['nick']) ? $userInfo['nick'] : '';
        }

        $results = [];
        $count = count($rotationList);
        for($i = 0; $i <= $count; $i++) {
            $results[] = "住友: " . self::hideNick($rotationList[array_rand($rotationList)]) . " 拼团成功";
        }

        return $results;

    }


    /**
     * @desc 商品详情页拼团信息
     * @note 展示逻辑：组团人数多靠前 组团人数相同时，临近结束靠前
     * @param $goodsId
     * @param int $limit
     * @return array
     */
    public static function goodsDetail($goodsId, $limit = 3)
    {
        $response = [];
        //查询正在进行的团购活动id
        $groupId = self::getPinTuanIdByGoodsId($goodsId);

        //查询正在进行的团购
        $info = GroupJoin::select('*', [
            'AND' => [
                'group_id' => $groupId,
                'end_time[>]' => date('Y-m-d H:i:s'),
                'status' => GroupJoin::STATUS_ING
            ],
            'ORDER' => [
                'join_member' => 'DESC',
                'start_time' => 'ASC',
            ],
            "LIMIT" => $limit
        ]);
        $info = self::formatInfo($info);

        //格式化
        if ($info) {
            foreach ($info as $value) {
                $userInfo = User::getUserInfo($value['uid']);
                $response[] = [
                    'team_join_id' => $value['id'],
                    'nick' => isset($userInfo['nick']) ? self::hideNick($userInfo['nick']) : '',
                    'avatar' => isset($userInfo['avatar']) ? $userInfo['avatar'] : '',
                    'last_member_num' => $value['last_member_num'],
                    'time_down' => [
                        'sys_time' => $value['sys_time'],
                        'begin_time' => $value['begin_time'],
                        'end_time' => $value['end_time'],
                    ],
                ];
            }
        }
        return $response;
    }

    /**
     * @desc 删除取消订单的团购
     * @param $order_no string 订单编号
     * @return int
     * @throws \Exception
     */
    public static function delGroup($order_no)
    {
        $groupJoinMemberRs = 0;
        try {
            $info = GroupJoinMember::getInfoByOrderNo($order_no);
            if (!$info) {
                throw new \Exception('团购成员信息不存在');
            }
            $groupJoinMemberRs = GroupJoinMember::update(["status" => GroupJoin::STATUS_DEL], $condition = [
                'id' => $info['id'],
                'status' => GroupJoin::STATUS_WAIT_PAY
            ])->rowCount();
            self::infoLog('删除取消订单的团购', ['request' => func_get_args(), 'response' => $groupJoinMemberRs]);
        } catch (\Exception $e) {
            $file = $e->getFile();
            $line = $e->getLine();
            $message = $e->getMessage();
            $trace = compact('file', 'line', 'message');
            self::errorLog('删除取消订单的团购', ['request' => func_get_args(), 'response' => compact('info', 'trace')]);
        }
        return $groupJoinMemberRs;
    }

    public static function getOrderGroupStatus($order_no)
    {
        //查询子订单号
        $order_no = is_array($order_no) ? $order_no : [$order_no];
        $select_order = [];
        $parent_order = [];
        foreach ($order_no as $v) {
            $isParentOrder = OrderNo::isParentNo($v);
            if ($isParentOrder) {
                $parent_order[] = $v;
            } else {
                $select_order[] = $v;
            }
        }

        if ($parent_order) {
            $list = Order::select('order_no', ['order_parent_no' => $parent_order]);
            $select_order = array_merge($select_order, $list);
        }

        $info = GroupJoinMember::getAdminInfoByOrderNos($select_order);
        $response = [];
        if ($info) {
            foreach ($info as $v) {
                $response[$v['order_no']] = [
                    'status' => GroupJoin::STATUS_DESC_MAP[$v['status']],
                    'group_join_id' => $v['group_join_id'],
                ];
            }
        }
        return $response;
    }

    /**
     * @desc 查询参与的团购id
     * @param $order_no string 订单编号
     * @param $uid
     * @return array
     * @throws \Exception
     */
    public static function getTeamId($order_no, $uid)
    {
        try {
            //查询子订单号
            $order_no = Order::getOrderNoList($order_no);
            $info = GroupJoinMember::getInfoByOrderNo($order_no);
            if (!$info) {
                throw new \Exception('团购成员信息不存在');
            }
            if ($info['uid'] != $uid) {
                throw new \Exception('用户权限不足');
            }
            $response = self::team_join_info($info['group_join_id'], $uid);
            self::infoLog('查询参与的团购id', compact('order_no', 'uid', 'response'));
            return $response;
        } catch (\Exception $e) {
            $file = $e->getFile();
            $line = $e->getLine();
            $message = $e->getMessage();
            $trace = compact('file', 'line', 'message');
            self::errorLog('查询参与的团购id', compact('order_no', 'uid', 'trace'));
            if (isset($info['group_join_id'])) {
                return ['team_join_id' => $info['group_join_id']];
            } else {
                return null;
            }
        }
    }

    /**
     * @desc 拼团详情页
     * @param $group_join_id int 参团id
     * @param $uid
     * @return array
     * @throws \Exception
     */
    public static function infoPage($group_join_id, $uid)
    {
        //查询参团信息
        $groupJoinInfo = self::getGroupJoinInfo($group_join_id);
        if (!$groupJoinInfo) {
            throw new \Exception('参与的团购信息不存在');
        }
        //参团信息
        $team_join_info = self::team_join_info($group_join_id, $uid);
        $goods_id = $groupJoinInfo['goods_id'];

        //开团信息
        $team_info = self::team_info($goods_id);
        $team_info = $team_info ? $team_info : [
            'goods_id' => $goods_id,
            'activity_id' => $groupJoinInfo['group_id'],
            'activity_status' => PinTuan::STATUS_DELETE,
        ];

        $goods = Goods::Goods($goods_id, Goods::FIELDS_GROUP_PINTUAN_DETAIL, $parameter = ['activity_type' => ActivityInfo::ACTIVITY_TYPE_PINTUAN, 'area_code' => 0]);
//        $groupMap = sPinTuan::getCurrentList([$goods_id]);
//        $actual_min_price = isset($groupMap[$goods_id]['min_price']) ? $groupMap[$goods_id]['min_price'] : $goods['actual_min_price'];
//        $actual_max_price = isset($groupMap[$goods_id]['max_price']) ? $groupMap[$goods_id]['max_price'] : $goods['actual_max_price'];

        $oldPrice = PinTuanSku::getList(['pintuan_id' => $groupJoinInfo['group_id']], 'activity_price');

        //商品信息
        $goods_info = [
            'id' => $goods['id'], //商品id
            'cover_img' => $goods['cover_img'], //商品图
            'title' => $goods['title'], //商品标题
            'ori_min_price' => $team_info['ori_min_price'] > 0 ? $team_info['ori_min_price'] : $goods['ori_min_price'], //商品价格
            'ori_max_price' => $team_info['ori_max_price'] > 0 ? $team_info['ori_max_price'] : $goods['ori_max_price'], //商品价格
            'actual_min_price' => min($oldPrice), //拼团价格
            'actual_max_price' => max($oldPrice), //拼团价格
            'active_icon_list' => $goods['active_icon_list'], //标签：拼团标签（N人团）；包邮标签（若不包邮则不展示）
            'goods_detail' => $goods['goods_detail'], //商品详情
            'goods_detail_h5' => $goods['goods_detail_h5'], //商品详情
            'actual_stock' => $goods['actual_stock'], //真实库存
            'activity_stock' => $goods['activity_stock'], //活动库存
        ];

        self::infoLog('拼团详情页debug', ['request' => func_get_args(), 'response' => compact('groupJoinInfo', 'team_join_info', 'team_info')]);

        return compact('goods_info', 'team_join_info', 'team_info');
    }

    /**
     * @desc 创建订单失败回调
     * @param $order_no string 订单号
     */
    public static function createOrderFail($order_no)
    {
        try {
            $info = GroupJoinMember::getInfoByOrderNo($order_no);
            if ($info) {
                $role = $info['role'];
                $group_join_id = $info['group_join_id'];
                //删除团购成员
                GroupJoinMember::delete([
                    'order_no' => $order_no,
                    'status' => GroupJoin::STATUS_WAIT_PAY
                ]);

                //删除团购信息
                if ($role == GroupJoinMember::ROLE_LEADER) {
                    GroupJoin::delete([
                        'id' => $group_join_id,
                        'status' => GroupJoin::STATUS_WAIT_PAY
                    ]);
                }
            }
            self::infoLog('创建订单失败回调', ['request' => compact('order_no')]);
        } catch (\Exception $e) {
            $trace = [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'message' => $e->getMessage(),
            ];
            self::errorLog('创建订单失败回调', ['request' => compact('order_no'), 'response' => compact('info', 'trace')]);
        }
    }

    /**
     * @desc 检查库存是否充足
     * @param $stockMap
     * @param $checkData array 检查数据
     * @return bool
     * @throws \Exception
     */
    public static function checkStock($stockMap, array $checkData)
    {
        $actualStockMap = $stockMap['actual_stock'];
        $activityStockMap = $stockMap['activity_stock'];
        $stockCheck = true;
        foreach ($actualStockMap as $key => $value) {
            //如果消耗库存大于活动库存,拼团失败
            if (isset($checkData[$key]) && ($checkData[$key] > $value)) {
                self::infoLog('校验实际库存是否充足', ["sku_id:{$key}库存不足,需要{$checkData[$key]},剩余{$value}"]);
                $stockCheck = false;
                break;
            }
        }
        if ($stockCheck) {
            foreach ($activityStockMap as $key => $value) {
                //如果消耗库存大于活动库存,拼团失败
                if (isset($checkData[$key]) && ($checkData[$key] > $value)) {
                    self::infoLog('校验活动库存是否充足', ["sku_id:{$key}库存不足,需要{$checkData[$key]},剩余{$value}"]);
                    $stockCheck = false;
                    break;
                }
            }
        }
        return $stockCheck;
    }

    /**
     * @desc 查询拼团活动库存和实际库存
     * @param $goodsId string 订单号
     * @return array
     * @throws \Exception
     */
    public static function getStockMap($goodsId)
    {
        $groupMap = sPinTuan::getCurrentList([$goodsId], false);
        $sku_ids = isset($groupMap[$goodsId]['skus']) ? array_column($groupMap[$goodsId]['skus'], 'sku_id') : [];
        $activity_stock_total = isset($groupMap[$goodsId]['skus']) ? array_column($groupMap[$goodsId]['skus'], 'actual_stock', 'sku_id'): [];
        $stock = Sku::Sku([$goodsId], Sku::FIELDS_GROUP_ALL, ['activity_type' => ActivityInfo::ACTIVITY_TYPE_PINTUAN]);
        $stock_item = isset($stock[$goodsId]) ? $stock[$goodsId] : [];
        $actual_stock_total = isset($stock_item['base']['goods_stock']) ? $stock_item['base']['goods_stock'] : [];

        $actual_stock = [];
        $activity_stock = [];
        foreach ($sku_ids as $sku_id) {
            if (isset($actual_stock_total[$sku_id])) {
                $actual_stock[$sku_id] = $actual_stock_total[$sku_id];
            }
            if (isset($activity_stock_total[$sku_id])) {
                $activity_stock[$sku_id] = $activity_stock_total[$sku_id];
            }
        }
        $stockMap = compact('actual_stock', 'activity_stock');
        self::infoLog('查询拼团活动库存和实际库存DEBUG', ['查询活动库存', $stockMap, $groupMap, $stock]);
        return $stockMap;
    }

    private static function checkPayGroupStock($group_join_id, $goodsId)
    {
        //查询成团所消耗的库存
        $joinMember = GroupJoinMember::getJoinMember($group_join_id);

        $stockDecrease = [];
        $joinMemberOrderNo = [];
        $joinMemberMap = [];
        $checkCount = 0;
        if ($joinMember) {
            foreach ($joinMember as $v) {
                if ($v['order_no'] != '') {
                    $sku_id = $v['sku_id'];
                    $checkCount += $v['buy_num'];
                    $stockDecrease[$sku_id] = isset($stockDecrease[$sku_id]) ? $stockDecrease[$sku_id] + $v['buy_num'] : $v['buy_num'];
                    $joinMemberOrderNo[] = $v['order_no'];
                    $joinMemberMap[$v['order_no']] = $v;
                }
            }
        }
        self::infoLog('后台手动成团DEBUG', ['查询成团所消耗的库存', $joinMemberMap, $joinMemberOrderNo]);

        //校验库存是否充足
        $stockMap = self::getStockMap($goodsId);
        $totalActualStock = array_sum($stockMap['actual_stock']);
        $totalActivityStock = array_sum($stockMap['activity_stock']);
        //校验总库存
        $checkActual = $checkCount > $totalActualStock;
        $checkActivity = $checkCount > $totalActivityStock;
        self::infoLog("后台手动成团DEBUG::校验总库存是否充足", compact('checkCount', 'checkActual', 'checkActivity'));
        if ($checkActual || $checkActivity) {
            $stockCheck = false;
        } else {
            $stockCheck = self::checkStock($stockMap, $stockDecrease);
        }
        return compact('stockCheck', 'joinMemberMap', 'joinMemberOrderNo');
    }

    /**
     * @desc 后台手动成团
     * @param $group_join_id string 参团id
     * @throws \Exception
     */
    public static function adminPayGroup($group_join_id)
    {
        try {
            $params = GroupJoin::action(function() use ($group_join_id){
                $refund_order_no = [];
                $success_order_no = [];
                $refund_reason = 0;
                $groupJoinInfo = GroupJoin::select_master('*', \Medoo\Medoo::raw(' where id=:id for update',[':id' => $group_join_id]));
                $groupJoinInfo = !empty($groupJoinInfo) ? current($groupJoinInfo) : [];
                if (!$groupJoinInfo) {
                    throw new \Exception('操作的参团信息不存在');
                }
                if ($groupJoinInfo['type'] == PinTuan::TYPE_NEW) {
                    throw new \Exception('新人团不允许手动成团');
                }
                if ($groupJoinInfo['status'] != GroupJoin::STATUS_ING) {
                    throw new \Exception('操作的参团信息状态不是进行中');
                }
                //还差多少成员成团
                $lastMember = $groupJoinInfo['member'] - $groupJoinInfo['join_member'];
                if ($lastMember <= 0) {
                    throw new \Exception('参团成员已满');
                }

                $goodsId = $groupJoinInfo['goods_id'];
                $groupId = $groupJoinInfo['group_id'];
                $info = [
                    'goods_id' => $goodsId,
                    'group_id' => $groupId,
                ];

                //检查当前活动是否结束
                $alive = self::getAliveByGroupId($groupId) > 0;

                if (!$alive) {
                    //查询成团的订单
                    $joinMember = GroupJoinMember::getJoinMember($group_join_id);

                    $joinMemberOrderNo = [];
                    if ($joinMember) {
                        foreach ($joinMember as $v) {
                            if ($v['order_no'] != '') {
                                $joinMemberOrderNo[] = $v['order_no'];
                            }
                        }
                    }

                    //更新拼团数据状态
                    self::fail_group_info($group_join_id, GroupJoin::FAIL_REASON_OVER_TIME);

                    //退款当前团购
                    $refund_order_no = $joinMemberOrderNo;
                    $refund_reason = GroupJoin::FAIL_REASON_OVER_TIME;

                    self::infoLog('后台手动成团DEBUG', ['活动结束退款', $refund_order_no, $refund_reason, $info]);
                } else {
                    //检查库存
                    $rs = self::checkPayGroupStock($group_join_id, $goodsId);
                    $stockCheck = $rs['stockCheck'];
                    $joinMemberMap = $rs['joinMemberMap'];
                    $joinMemberOrderNo = $rs['joinMemberOrderNo'];
                    if (!$stockCheck) {
                        //更新拼团数据状态
                        self::fail_group_info($group_join_id);

                        //退款当前团购
                        $refund_order_no = $joinMemberOrderNo;
                        $refund_reason = GroupJoin::FAIL_REASON_NOT_ENOUGH_STOCK;

                        self::infoLog('后台手动成团DEBUG', ['库存不足退款1', $refund_order_no, $refund_reason, $info]);
                    } else {
                        //添加新成员信息
                        for ($i = 0; $i < $lastMember; $i ++) {
                            $rs = GroupJoinMember::addData(
                                $group_join_id,
                                $groupJoinInfo['group_id'],
                                $groupJoinInfo['type'],
                                0,
                                $groupJoinInfo['shop_id'],
                                $groupJoinInfo['goods_id'],
                                0,
                                $role = GroupJoinMember::ROLE_PARTNER,
                                '',
                                GroupJoin::STATUS_ING,
                                json_encode(self::robot(), JSON_UNESCAPED_UNICODE)
                            );
                            if (!$rs) {
                                throw new \Exception('添加新成员信息失败');
                            }
                        }

                        //累加参团成员
                        $data = ["join_member[+]" => $lastMember];

                        //更新团购信息,累加参团成员
                        $condition = \Medoo\Medoo::raw(' where id=:id and member > join_member and status = :status',
                            [
                                ':id' => $group_join_id,
                                ':status' => GroupJoin::STATUS_ING
                            ]);
                        $groupJoinRs = GroupJoin::update($data, $condition)->rowCount();
                        if (!$groupJoinRs) {
                            throw new \Exception('更新参团信息失败');
                        }

                        self::infoLog('后台手动成团DEBUG', ['更新参团信息成功', $groupJoinRs, $groupJoinInfo]);
                        //成团之后的处理
                        $incrStockOrderNos = [];
                        if ($stockCheck) {
                            //扣除库存
                            $rs = self::success_deduct_stock($joinMemberOrderNo, $joinMemberMap, $stockCheck);
                            $stockCheck = $rs['stockCheck'];
                            $incrStockOrderNos = $rs['incrStockOrderNos'];

                            if ($stockCheck) {
                                //拼团成功通知
                                self::successGroup($groupJoinInfo['id']);
                                $success_order_no = $joinMemberOrderNo;
                                self::infoLog('后台手动成团DEBUG', ['拼团成功订单', $success_order_no, $info]);
                            }
                        }

                        //扣除库存失败的处理
                        if (!$stockCheck) {
                            //返回扣除的库存
                            try {
                                if ($incrStockOrderNos) {
                                    $rs = Stock::IncrStockByOrderNos($incrStockOrderNos);
                                    self::infoLog('后台手动成团DEBUG::返还库存', ['request' => compact('incrStockOrderNos'), 'response' => $rs]);
                                }
                            } catch (\Exception $e) {
                                self::errorLog('后台手动成团DEBUG::返还库存', ['request' => compact('incrStockOrderNos'), 'response' => [$e->getFile(), $e->getLine(), $e->getMessage(), $e->getTrace()]]);
                            }

                            //更新拼团数据状态
                            self::fail_group_info($group_join_id);

                            //退款当前团购
                            $refund_order_no = $joinMemberOrderNo;
                            $refund_reason = GroupJoin::FAIL_REASON_NOT_ENOUGH_STOCK;

                            self::infoLog('后台手动成团DEBUG', ['库存不足退款', $refund_order_no, $refund_reason, $info]);
                        }
                    }
                }

                return compact('refund_order_no', 'success_order_no', 'refund_reason');
            });
            $refund_order_no = $params['refund_order_no']; //退款订单号集合
            $refund_reason = $params['refund_reason']; //退款订单号集合
            $success_order_no = $params['success_order_no']; //成功拼团通知

            //失败通知及退款
            self::fail_send_msg_and_refund($refund_order_no, $refund_reason);

            //成功通知
            self::success_send_msg($success_order_no);

            self::infoLog('后台手动成团', ['request' => func_get_args(), 'response' => compact('groupJoinInfo', 'groupJoinMemberRs', 'groupJoinRs', 'params')]);
        } catch (\Exception $e) {
            $trace = [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'message' => $e->getMessage(),
            ];
            self::errorLog('后台手动成团失败', ['request' => func_get_args(), 'response' => compact('groupJoinInfo', 'groupJoinMemberRs', 'groupJoinRs', 'trace')]);
            throw $e;
        }
    }

    /**
     * @desc 支付回调
     * @param $order_no string 订单号
     * @throws \Exception
     */
    public static function payGroup($order_no)
    {
        try {
            $info = GroupJoinMember::getInfoByOrderNo($order_no);
            if (!$info) {
                throw new \Exception('团购成员信息不存在');
            }

            $id = $info['id'];
            $group_join_id = $info['group_join_id'];
            $role = $info['role'];

            $params = GroupJoin::action(function() use ($group_join_id, $role, $order_no, $id){
                $refund_order_no = [];
                $refund_reason = 0;
                $success_order_no = [];
                $info = [];

                $groupJoinInfo = GroupJoin::select_master('*', \Medoo\Medoo::raw(' where id=:id for update',[':id' => $group_join_id]));
                $groupJoinInfo = !empty($groupJoinInfo) ? current($groupJoinInfo) : [];
                if (!$groupJoinInfo) {
                    throw new \Exception('参与的团购信息不存在');
                }

                //更新成员信息
                $groupJoinMemberRs = GroupJoinMember::update(["status" => GroupJoin::STATUS_ING, "start_time" => date('Y-m-d H:i:s')], $condition = [
                    'id' => $id,
                    'status' => GroupJoin::STATUS_WAIT_PAY
                ])->rowCount();

                //更新成功后处理团购信息
                if (!$groupJoinMemberRs) {
                    throw new \Exception('更新支付团成员信息失败');
                }

                //累加参团成员
                $data = ["join_member[+]" => 1];

                //如果是团长,更新团购状态和开始,截止时间
                if ($role == GroupJoinMember::ROLE_LEADER) {
                    $group_cycle = $groupJoinInfo['group_cycle'];
                    $end_time = strtotime($groupJoinInfo['end_time']);
                    $data['status'] = GroupJoin::STATUS_ING;
                    $data['start_time'] = date('Y-m-d H:i:s');
                    $new_end_time = time() + $group_cycle;
                    if ($new_end_time < $end_time) {
                        $data['end_time'] = date('Y-m-d H:i:s', $new_end_time);
                    }
                }
                //更新团购信息
                $condition = \Medoo\Medoo::raw(' where id=:id and member > join_member and status IN (:status,:pay_status)',
                    [
                        ':id' => $group_join_id,
                        ':status' => GroupJoin::STATUS_ING,
                        ':pay_status' => GroupJoin::STATUS_WAIT_PAY,
                    ]);
                $groupJoinRs = GroupJoin::update($data, $condition)->rowCount();
                if (!$groupJoinRs) {
                    self::infoLog('更新团购信息失败退款', ['request' => func_get_args(), 'response' => compact('order_no', 'groupJoinInfo', 'groupJoinRs', 'groupJoinMemberRs')]);
                    //修改成员支付状态
                    $groupJoinMemberData = [
                        "status" => GroupJoin::STATUS_FAIL_OUT_JOIN
                    ];
                    GroupJoinMember::update($groupJoinMemberData, $condition = [
                        'group_join_id' => $groupJoinInfo['id'],
                        'status' => [
                            GroupJoin::STATUS_ING
                        ]
                    ]);

                    //退款当前团购
                    $refund_order_no = [$order_no];
                    $refund_reason = GroupJoin::FAIL_REASON_NOT_ENOUGH_STOCK;
                } else {

                    $goodsId = $groupJoinInfo['goods_id'];
                    $groupId = $groupJoinInfo['group_id'];
                    $info = [
                        'goods_id' => $goodsId,
                        'group_id' => $groupId,
                    ];


                    $isSuccess = ($groupJoinInfo['member'] - $groupJoinInfo['join_member']) == 1;

                    self::infoLog('支付回调DEBUG', ['更新团购信息成功', $groupJoinRs, $groupJoinInfo, $isSuccess]);

                    //查询成团所消耗的库存
                    $joinMember = GroupJoinMember::getJoinMember($group_join_id);

                    $stockDecrease = [];
                    $joinMemberOrderNo = [];
                    $joinMemberMap = [];
                    if ($joinMember) {
                        foreach ($joinMember as $v) {
                            if ($v['order_no'] != '') {
                                $sku_id = $v['sku_id'];
                                $stockDecrease[$sku_id] = isset($stockDecrease[$sku_id]) ? $stockDecrease[$sku_id] + 1 : 1;
                                $joinMemberOrderNo[] = $v['order_no'];
                                $joinMemberMap[$v['order_no']] = $v;
                            }
                        }
                    }
                    self::infoLog('支付回调DEBUG', ['查询成团所消耗的库存', $joinMemberMap, $joinMemberOrderNo]);

                    //检查当前活动是否结束
                    $alive = self::getAliveByGroupId($groupId) > 0;
                    self::infoLog('支付回调DEBUG', ['检查当前活动是否结束', $alive]);
                    if (!$alive) {
                        //更新拼团数据状态
                        self::fail_group_info($group_join_id, GroupJoin::FAIL_REASON_OVER_TIME);

                        //退款当前团购
                        $refund_order_no = $joinMemberOrderNo;
                        $refund_reason = GroupJoin::FAIL_REASON_OVER_TIME;

                        self::infoLog('支付回调DEBUG', ['活动结束退款', $refund_order_no, $refund_reason, $info]);
                    } else {
                        //校验库存是否充足
                        $stockMap = self::getStockMap($goodsId);
                        $totalActualStock = array_sum($stockMap['actual_stock']);
                        $totalActivityStock = array_sum($stockMap['activity_stock']);
                        //校验总库存
                        $checkMember = $groupJoinInfo['member'];
                        $checkActual = $checkMember > $totalActualStock;
                        $checkActivity = $checkMember > $totalActivityStock;
                        self::infoLog("支付回调DEBUG::校验总库存是否充足", compact('checkMember', 'checkActual', 'checkActivity'));
                        if ($checkActual || $checkActivity) {
                            $stockCheck = false;
                        } else {
                            $stockCheck = self::checkStock($stockMap, $stockDecrease);
                        }

                        //成团之后的处理
                        $incrStockOrderNos = [];
                        if ($isSuccess && $stockCheck) {
                            //查询父订单号
                            $pOrderNo = mOrder::selectOrderByOrderNo($joinMemberOrderNo);
                            $pOrderNo = !empty($pOrderNo) ? array_column($pOrderNo, 'order_parent_no', 'order_no') : [];
                            self::infoLog('支付回调DEBUG', ['查询父订单', $joinMemberOrderNo, $pOrderNo]);

                            //扣除库存
                            foreach ($pOrderNo as $k => $v) {
                                $joinMemberInfo = isset($joinMemberMap[$k]) ? $joinMemberMap[$k] : [];
                                $buyNums = [];
                                if ($joinMemberInfo) {
                                    $buyNums[$k][] = [
                                        "goods_id" => $joinMemberInfo['goods_id'],
                                        "sku_id" => $joinMemberInfo['sku_id'],
                                        "buy_num" => $joinMemberInfo['buy_num'],
                                        'promo_type' => ActivityInfo::ACTIVITY_TYPE_PINTUAN,
                                        'promo_id' => $joinMemberInfo['group_id']
                                    ];
                                    try {
                                        $stockCheck = Stock::DecrStockByOrderNo($v, $buyNums);
                                        if ($stockCheck) {
                                            $incrStockOrderNos[] = $k;
                                        }
                                        self::infoLog('支付回调::扣减库存', ['request' => compact('v', 'buyNums'), 'response' => $stockCheck]);
                                    } catch (\Exception $e) {
                                        $stockCheck = false;
                                        self::errorLog('支付回调::扣减库存', ['request' => compact('v', 'buyNums'), 'response' => [$e->getFile(), $e->getLine(), $e->getMessage(), $e->getTrace()]]);
                                    }
                                }
                            }
                            self::infoLog('支付回调DEBUG', ['扣除库存', $stockCheck]);

                            if ($stockCheck) {
                                //拼团成功通知
                                self::successGroup($groupJoinInfo['id']);
                                $success_order_no = $joinMemberOrderNo;
                                self::infoLog('支付回调DEBUG', ['拼团成功订单', $joinMemberOrderNo, $info]);
                            }
                        }

                        //库存不足的处理
                        if (!$stockCheck) {
                            //返回扣除的库存
                            try {
                                if ($incrStockOrderNos) {
                                    $rs = Stock::IncrStockByOrderNos($incrStockOrderNos);
                                    self::infoLog('支付回调::返还库存', ['request' => compact('incrStockOrderNos'), 'response' => $rs]);
                                }
                            } catch (\Exception $e) {
                                self::errorLog('支付回调::返还库存', ['request' => compact('incrStockOrderNos'), 'response' => [$e->getFile(), $e->getLine(), $e->getMessage(), $e->getTrace()]]);
                            }

                            $groupJoinData = [
                                "status" => GroupJoin::STATUS_FAIL,
                                'reason' => GroupJoin::FAIL_REASON_NOT_ENOUGH_STOCK
                            ];
                            $groupJoinMemberData = [
                                "status" => GroupJoin::STATUS_FAIL
                            ];
                            GroupJoin::update($groupJoinData, $condition = [
                                'id' => $groupJoinInfo['id'],
                                'status' => [
                                    GroupJoin::STATUS_ING,
                                    GroupJoin::STATUS_SUCCESS,
                                ]
                            ]);
                            GroupJoinMember::update($groupJoinMemberData, $condition = [
                                'group_join_id' => $groupJoinInfo['id'],
                                'status' => [
                                    GroupJoin::STATUS_ING,
                                    GroupJoin::STATUS_SUCCESS,
                                ]
                            ]);
                            self::infoLog('支付回调DEBUG', ['更新库存不足团购信息成功']);

                            //退款当前团购
                            $refund_order_no = $joinMemberOrderNo;
                            $refund_reason = GroupJoin::FAIL_REASON_NOT_ENOUGH_STOCK;

                            self::infoLog('支付回调DEBUG', ['库存不足退款', $refund_order_no, $refund_reason, $info]);
                        }
                    }
                }
                return compact('refund_order_no', 'success_order_no', 'info', 'refund_reason');
            });
            //参团之后处理
            self::_async($params);
            self::infoLog('支付回调', ['request' => func_get_args(), 'response' => compact('groupJoinInfo', 'groupJoinMemberRs', 'groupJoinRs', 'params')]);
        } catch (\Exception $e) {
            $trace = [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'message' => $e->getMessage(),
            ];
            self::errorLog('支付回调失败', ['request' => func_get_args(), 'response' => compact('groupJoinInfo', 'groupJoinMemberRs', 'groupJoinRs', 'trace')]);
        }
    }

    public static function successGroup($group_join_id)
    {
        try {
            $groupJoinData = [
                "status" => GroupJoin::STATUS_SUCCESS,
                'success_time' => date("Y-m-d H:i:s")
            ];
            $groupJoinMemberData = [
                "status" => GroupJoin::STATUS_SUCCESS
            ];

            //修改团信息状态
            $groupJoinRs = GroupJoin::update($groupJoinData, $condition = [
                'id' => $group_join_id,
                'status' => GroupJoin::STATUS_ING
            ])->rowCount();
            if (!$groupJoinRs) {
                throw new \Exception('更新团购信息失败[拼团成功]');
            }
            $groupJoinMemberRs = GroupJoinMember::update($groupJoinMemberData, $condition = [
                'group_join_id' => $group_join_id,
                'status' => GroupJoin::STATUS_ING
            ])->rowCount();
            if (!$groupJoinMemberRs) {
                throw new \Exception('更新团成员信息失败[拼团成功]');
            }
            self::infoLog('支付回调异步DEBUG[更新db拼团成功]', compact('group_join_id'));
        } catch (\Exception $e) {
            $trace = [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'message' => $e->getMessage(),
            ];
            self::errorLog('支付回调异步DEBUG[更新db拼团成功]', compact('group_join_id', 'trace'));
        }
    }

    public static function getOrderGoodsInfo($order_no)
    {
        //查询商品名称和拼团价
        $order_goods = OrderGoods::getFieldsByOrderNo($order_no);
        $orderForm = Order::getOrderInfoList(['order_no' => $order_no], [], [], false, ['from_source', 'order_no', 'status']);
        $orderNoMap = empty($orderForm) ? [] : array_column($orderForm, 'from_source', 'order_no');
        $orderStatusMap = empty($orderForm) ? [] : array_column($orderForm, 'status', 'order_no');
        $order_goods = is_array($order_goods) ? array_column($order_goods, null, 'order_no') : [];
        foreach ($order_goods as $key => $value) {
            $order_goods[$key]['from_source'] = isset($orderNoMap[$value['order_no']]) ? $orderNoMap[$value['order_no']] : '';
            $order_goods[$key]['status'] = isset($orderStatusMap[$value['order_no']]) ? $orderStatusMap[$value['order_no']] : '';
        }
        return $order_goods;
    }

    /**
     * @desc 支付回调异步操作
     * @param array $params
     * @throws \Exception
     */
    public static function _async_payGroup($params)
    {
        //异步处理
        $refund_order_no = $params['refund_order_no']; //退款订单号集合
        $refund_reason = $params['refund_reason']; //退款原因
        $success_order_no = $params['success_order_no']; //成功拼团通知
        $info = $params['info']; //库存不足或者成功拼团,检查其它团购
        self::infoLog('支付回调异步操作DEBUG', compact('params'));

        //失败通知及退款
        self::fail_send_msg_and_refund($refund_order_no, $refund_reason);

        //成功通知
        self::success_send_msg($success_order_no);

        //校验其它团购库存及退款
        if (empty($info)) {
            return;
        }
        //检查其它团购库存状况
        $goodsId = $info['goods_id'];
        $groupId = $info['group_id'];
        //查询拼团活动库存和实际库存
        $stockMap = self::getStockMap($goodsId);
        $totalActualStock = array_sum($stockMap['actual_stock']);
        $totalActivityStock = array_sum($stockMap['activity_stock']);

        //查询所需库存不够的拼团
        $waitCancelGroup = GroupJoin::getWaitCancel($groupId);
        self::infoLog('支付回调异步操作::查询所需库存不够的拼团DEBUG', compact('groupId', 'waitCancelGroup', 'stockMap', 'totalActualStock', 'totalActivityStock'));
        if ($waitCancelGroup) {
            //查询团成员信息
            $waitCancelGroupIds = array_column($waitCancelGroup, 'id', 'id');
            $memberMap = array_column($waitCancelGroup, 'member', 'id');
            $joinMember = GroupJoinMember::getJoinMember(array_values($waitCancelGroupIds));

            $joinMemberOrderNoMap = [];
            $joinMemberMap = [];
            foreach ($joinMember as $member) {
                $joinMemberMap[$member['group_join_id']][] = $member;
            }
            foreach ($joinMemberMap as $key => $members) {
                $stockDecrease = [];
                $checkMember = isset($memberMap[$key]) ? $memberMap[$key] : 0;
                foreach ($members as $member) {
                    $sku_id = $member['sku_id'];
                    $stockDecrease[$sku_id] = isset($stockDecrease[$sku_id]) ? $stockDecrease[$sku_id] + 1 : 1;
                    $joinMemberOrderNoMap[$member['group_join_id']][] = $member['order_no'];
                }
                //校验总库存
                $checkActual = $checkMember > $totalActualStock;
                $checkActivity = $checkMember > $totalActivityStock;
                if ($checkActual || $checkActivity) {
                    self::infoLog("[group_join_id:$key]校验结果1", compact('checkActual', 'checkActivity'));
                    continue;
                }
                //校验sku库存
                $check = self::checkStock($stockMap, $stockDecrease);
                if (!$check) {
                    self::infoLog("[group_join_id:$key]校验结果2", compact('checkActual', 'checkActivity', 'check'));
                    continue;
                }
                //去掉满足库存的团购
                unset($waitCancelGroupIds[$key]);
                self::infoLog("[group_join_id:$key]校验结果3", compact('checkActual', 'checkActivity', 'check'));
            }
            $groupJoinData = [
                "status" => GroupJoin::STATUS_FAIL,
                'reason' => GroupJoin::FAIL_REASON_NOT_ENOUGH_STOCK
            ];
            $groupJoinMemberData = [
                "status" => GroupJoin::STATUS_FAIL
            ];
            self::infoLog('支付回调异步操作::取消库存不够的拼团DEBUG', compact('waitCancelGroupIds'));
            foreach ($waitCancelGroupIds as $v) {
                try {
                    $id = $v;
                    GroupJoin::action(function() use ($id, $groupJoinData, $groupJoinMemberData){
                        //修改团信息状态
                        $groupJoinRs = GroupJoin::update($groupJoinData, $condition = [
                            'id' => $id,
                            'status' => [
                                GroupJoin::STATUS_ING,
                            ]
                        ])->rowCount();
                        if (!$groupJoinRs) {
                            throw new \Exception('更新团购信息失败[所需库存不够的拼团]');
                        }
                        $groupJoinMemberRs = GroupJoinMember::update($groupJoinMemberData, $condition = [
                            'group_join_id' => $id,
                            'status' => [
                                GroupJoin::STATUS_ING,
                            ]
                        ])->rowCount();
                        if (!$groupJoinMemberRs) {
                            throw new \Exception('更新团成员信息失败[所需库存不够的拼团]');
                        }
                    });

                    //退款,推送消息
                    $order_no_array = isset($joinMemberOrderNoMap[$id]) ? $joinMemberOrderNoMap[$id] : [];
                    self::fail_send_msg_and_refund($order_no_array);
                } catch (\Exception $e) {
                    $trace = [
                        'file' => $e->getFile(),
                        'line' => $e->getLine(),
                        'message' => $e->getMessage(),
                    ];
                    self::errorLog('支付回调异步退款操作[库存不够的拼团]', compact('id', 'trace'));
                }
            }
        }
    }

    private static function success_send_msg(array $success_order_no)
    {
        if ($success_order_no) {
            //查询商品名称和拼团价
            $order_goods = self::getOrderGoodsInfo($success_order_no);
            foreach ($success_order_no as $v) {
                try {
                    self::infoLog('支付回调成功通知DEBUG', [$v, $order_goods]);
                    $orderInfo = isset($order_goods[$v]) ? $order_goods[$v] : [];
                    if (!$orderInfo) {
                        throw new \Exception('订单信息不存在,订单号:'. $v);
                    }
                    if ($orderInfo['from_source'] == Order::FROM_SOURCE_APP) {
                        sTradeMsg::addMsg(sTradeMsg::MSG_CODE_502, $orderInfo['uid'], $sysMsg = [
                            'order_no' => $v,
                            'goods_title' =>$orderInfo['title'],
                            'img' => $orderInfo['cover_img_url'],
                        ]);
                        self::infoLog('支付回调DEBUG', ['app成功通知', $v]);
                    } else {
                        $noticeData = [
                            'keyword1' => $v,//订单号
                            'keyword2' => isset($orderInfo['title']) ? $orderInfo['title'] : '',//商品名称
                            'keyword3' => '¥' . (isset($orderInfo['buy_price']) ? $orderInfo['buy_price'] : '0'),//拼团价
                            'keyword4' => '恭喜，拼团成功！请等待商家发货',//备注
                        ];
                        sTradeMsg::sendMiniProgramGroupSuccessNotice($v, $noticeData);
                        self::infoLog('支付回调DEBUG', ['小程序成功通知', $v]);
                    }
                } catch (\Exception $e) {
                    $trace = [
                        'file' => $e->getFile(),
                        'line' => $e->getLine(),
                        'message' => $e->getMessage(),
                    ];
                    self::errorLog('支付回调异步退款操作', compact('v', 'trace'));
                }
            }
        }
    }

    private static function success_deduct_stock($joinMemberOrderNo, $joinMemberMap, $stockCheck)
    {
        $incrStockOrderNos = [];
        if ($joinMemberOrderNo) {
            //查询父订单号
            $pOrderNo = mOrder::selectOrderByOrderNo($joinMemberOrderNo);
            $pOrderNo = !empty($pOrderNo) ? array_column($pOrderNo, 'order_parent_no', 'order_no') : [];
            self::infoLog('后台手动成团DEBUG', ['查询父订单', $joinMemberOrderNo, $pOrderNo]);
            //扣除库存
            foreach ($pOrderNo as $k => $v) {
                $joinMemberInfo = isset($joinMemberMap[$k]) ? $joinMemberMap[$k] : [];
                $buyNums = [];
                if ($joinMemberInfo) {
                    $buyNums[$k][] = [
                        "goods_id" => $joinMemberInfo['goods_id'],
                        "sku_id" => $joinMemberInfo['sku_id'],
                        "buy_num" => $joinMemberInfo['buy_num'],
                        'promo_type' => ActivityInfo::ACTIVITY_TYPE_PINTUAN,
                        'promo_id' => $joinMemberInfo['group_id']
                    ];
                    try {
                        $stockCheck = Stock::DecrStockByOrderNo($v, $buyNums);
                        if ($stockCheck) {
                            $incrStockOrderNos[] = $k;
                        }
                        self::infoLog('扣减库存', ['request' => compact('v', 'buyNums'), 'response' => $stockCheck]);
                    } catch (\Exception $e) {
                        $stockCheck = false;
                        self::errorLog('扣减库存', ['request' => compact('v', 'buyNums'), 'response' => [$e->getFile(), $e->getLine(), $e->getMessage(), $e->getTrace()]]);
                    }
                }
            }
        }
        self::infoLog('扣减库存', ['扣除库存结果', $stockCheck, $incrStockOrderNos]);
        return compact('stockCheck', 'incrStockOrderNos');
    }

    private static function fail_send_msg_and_refund(array $refund_order_no, $refund_reason = GroupJoin::FAIL_REASON_NOT_ENOUGH_STOCK)
    {
        if (!empty($refund_order_no)) {
            //查询商品名称和拼团价
            $order_goods = self::getOrderGoodsInfo($refund_order_no);
            foreach ($refund_order_no as $v) {
                try {
                    $orderInfo = isset($order_goods[$v]) ? $order_goods[$v] : [];
                    if (!$orderInfo) {
                        throw new \Exception('订单信息不存在,订单号:'. $v);
                    }

                    //校验订单状态是否能退款
                    self::checkRefundOrder($orderInfo);

                    $refundOrderNo = OrderPinTuan::cancelAndRefundOrder($v);
                    self::infoLog('拼团订单退款操作', [$v]);

                    if ($orderInfo['from_source'] == Order::FROM_SOURCE_APP) {
                        sTradeMsg::addMsg(501, $orderInfo['uid'], $sysMsg = [
                            'refund_order_no' => $refundOrderNo,
                            'refund_amount' =>$orderInfo['pay_amount'],
                            'img' => $orderInfo['cover_img_url'],
                        ]);
                        self::infoLog('拼团订单退款操作', ['app失败通知及退款', $v]);
                    } else {
                        $noticeData = [
                            'keyword1' => $v,//订单号
                            'keyword2' => isset($orderInfo['title']) ? $orderInfo['title'] : '',//商品名称
                            'keyword3' => '¥' . (isset($orderInfo['buy_price']) ? $orderInfo['buy_price'] : '0'),//拼团价
                            'keyword4' => $refund_reason == GroupJoin::FAIL_REASON_NOT_ENOUGH_STOCK ? '来晚一步，别人已抢先成团' : '限时内未达到成团人数',//失败原因
                            'keyword5' => '¥' . (isset($orderInfo['pay_amount']) ? $orderInfo['pay_amount'] : '0') . ' 将退回你的支付账户，请注意查收',//备注
                        ];
                        sTradeMsg::sendMiniProgramGroupFailNotice($v, $noticeData);
                        self::infoLog('拼团订单退款操作', ['小程序失败通知及退款', $v]);
                    }
                } catch (\Exception $e) {
                    $trace = [
                        'file' => $e->getFile(),
                        'line' => $e->getLine(),
                        'message' => $e->getMessage(),
                    ];
                    self::errorLog('拼团订单退款操作', compact('v', 'trace'));
                }
            }
        }
    }

    private static function fail_group_info($group_join_id, $reason = GroupJoin::FAIL_REASON_NOT_ENOUGH_STOCK)
    {
        $groupJoinData = [
            "status" => GroupJoin::STATUS_FAIL,
            'reason' => $reason
        ];
        $groupJoinMemberData = [
            "status" => GroupJoin::STATUS_FAIL
        ];
        GroupJoin::update($groupJoinData, $condition = [
            'id' => $group_join_id,
            'status' => [
                GroupJoin::STATUS_ING,
                GroupJoin::STATUS_SUCCESS,
            ]
        ]);
        GroupJoinMember::update($groupJoinMemberData, $condition = [
            'group_join_id' => $group_join_id,
            'status' => [
                GroupJoin::STATUS_ING,
                GroupJoin::STATUS_SUCCESS,
            ]
        ]);
    }

    private static function checkRefundOrder($orderInfo)
    {
        //校验订单状态是否能退款
        if ($orderInfo['status'] != Order::STATUS_WAIT_SEND) {
            throw new \Exception('订单状态不正确,当前订单状态为:' . $orderInfo['status']);
        }
    }

    /**
     * @desc 取消团购异步操作
     * @param $order_no mixed 订单号集合
     * @throws \Exception
     */
    public static function _async_cancelGroup($order_no)
    {
        $order_no = is_array($order_no) ? $order_no : [$order_no];
        if (!empty($order_no)) {
            //查询商品名称和拼团价
            $order_goods = self::getOrderGoodsInfo($order_no);
            foreach ($order_no as $v) {
                try {
                    $orderInfo = isset($order_goods[$v]) ? $order_goods[$v] : [];

                    //校验订单状态是否能退款
                    self::checkRefundOrder($orderInfo);

                    $refundOrderNo = OrderPinTuan::cancelAndRefundOrder($v);
                    self::infoLog('系统取消团购异步退款操作', [$v]);

                    if ($orderInfo['from_source'] == Order::FROM_SOURCE_APP) {
                        sTradeMsg::addMsg(501, $orderInfo['uid'], $sysMsg = [
                            'refund_order_no' => $refundOrderNo,
                            'refund_amount' =>$orderInfo['pay_amount'],
                            'img' => $orderInfo['cover_img_url'],
                        ]);
                        self::infoLog('系统取消团购消息', ['app失败通知及退款', $v]);
                    } else {
                        $noticeData = [
                            'keyword1' => $v,//订单号
                            'keyword2' => isset($orderInfo['title']) ? $orderInfo['title'] : '',//商品名称
                            'keyword3' => '¥' . (isset($orderInfo['buy_price']) ? $orderInfo['buy_price'] : '0'),//拼团价
                            'keyword4' => '限时内未达到成团人数',//失败原因
                            'keyword5' => '¥' . (isset($orderInfo['pay_amount']) ? $orderInfo['pay_amount'] : '0') . ' 将退回你的支付账户，请注意查收',//备注
                        ];
                        sTradeMsg::sendMiniProgramGroupFailNotice($v, $noticeData);
                        self::infoLog('系统取消团购消息卡片操作', [$v]);
                    }
                } catch (\Exception $e) {
                    $trace = [
                        'file' => $e->getFile(),
                        'line' => $e->getLine(),
                        'message' => $e->getMessage(),
                    ];
                    self::errorLog('系统取消团购异步退款操作', compact('v', 'trace'));
                }
            }
        }
    }


    /**
     * @desc 开团,参团
     * @param $uid
     * @param $sku_id
     * @param $order_no string 订单号
     * @param $group_join_id int 参团id,开团为0
     * @throws \Exception
     */
    public static function createGroup($uid, $sku_id, $order_no, $group_join_id = 0)
    {
        try {
            if ($order_no == '') {
                throw new \Exception('订单号必须');
            }
            if ($group_join_id > 0) {
                self::joinOne($uid, $group_join_id, $sku_id, $order_no);
            } else {
                self::createOne($uid, $sku_id, $order_no);
            }
            self::infoLog('开团,参团创建', compact('uid', 'sku_id', 'order_no', 'group_join_id'));
        } catch (\Exception $e) {
            $trace = [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'message' => $e->getMessage(),
            ];
            self::errorLog('开团,参团创建', compact('uid', 'sku_id', 'order_no', 'group_join_id', 'trace'));
            throw $e;
        }
    }

    /**
     * @desc 开团,参团校验
     * @param $uid
     * @param $sku_id
     * @param $group_join_id int 参团id,开团为0
     * @throws \Exception
     */
    public static function checkGroup($uid, $sku_id, $group_join_id = 0)
    {
        try {
            if ($group_join_id > 0) {
                self::checkJoin($uid, $group_join_id);
            } else {
                self::checkCreate($uid, $sku_id);
            }
            self::infoLog('开团,参团校验', compact('uid', 'sku_id', 'group_join_id'));
        } catch (\Exception $e) {
            $trace = [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'message' => $e->getMessage(),
            ];
            self::errorLog('开团,参团校验', compact('uid', 'sku_id', 'group_join_id', 'trace'));
            throw $e;
        }
    }

    /**
     * @desc 开团
     * @param $uid
     * @param $sku_id
     * @param $order_no string 订单号
     * @return mixed
     * @throws \Exception
     */
    private static function createOne($uid, $sku_id, $order_no)
    {
        try {
            if ($uid <= 0) {
                throw new \Exception('开团前必须登录');
            }

            //校验创建团购
            $groupInfo = self::checkCreate($uid, $sku_id);
            $group_id = $groupInfo['group_id'];
            $type = $groupInfo['type'];
            $member = $groupInfo['member'];
            $group_buy_price = $groupInfo['group_buy_price'];
            $shop_id = $groupInfo['shop_id'];
            $goods_id = $groupInfo['goods_id'];
            $group_cycle = $groupInfo['group_cycle'];
            $end_time = $groupInfo['end_time'];

            //创建团购记录
            $join_id = GroupJoin::addData($group_id, $type, $member, $uid, $shop_id, $goods_id, $group_buy_price, $group_cycle, $end_time);
            if ($join_id <= 0) {
                throw new \Exception('创建团购记录失败');
            }

            //添加团购成员信息
            $rs = GroupJoinMember::addData($join_id, $group_id, $type, $uid, $shop_id, $goods_id, $sku_id, $role = GroupJoinMember::ROLE_LEADER, $order_no);
            if (!$rs) {
                throw new \Exception('添加团购成员信息失败');
            }
            self::infoLog('开团', ['request' => func_get_args(), 'response' => $join_id]);
        } catch (\Exception $e) {
            $trace = [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'message' => $e->getMessage(),
            ];
            self::errorLog('开团', ['request' => func_get_args(), 'response' => compact('groupInfo', 'join_id', 'trace')]);
            throw $e;
        }
    }

    private static function checkJoin($uid, $group_join_id)
    {
        //查询参团信息
        $groupJoinInfo = self::getGroupJoinInfo($group_join_id);
        if (!$groupJoinInfo) {
            throw new \Exception('参与的团购信息不存在');
        }
        if (!in_array($groupJoinInfo['status'], [GroupJoin::STATUS_ING])) {
            throw new \Exception('参与的团购不在进行中');
        }
        if ($groupJoinInfo['last_time'] == 0) {
            throw new \Exception('参与的团购已结束');
        }

        //查询用户是否参与该团购
        $unique = GroupJoinMember::getGroupJoinCount($uid, $group_join_id);
        if ($unique > 0) {
            throw new \Exception('抱歉，你已经参与当前团购哦');
        }

        //查询用户参与团购的场次
        $count = GroupJoinMember::getJoinCount($uid);
        if ($count >= self::JOIN_MAX) {
            throw new \Exception(vsprintf('抱歉，最多只可以参加%d场团购哦', [self::JOIN_MAX]));
        }

        //新用户判断 没有支付过 若拼团失败不算支付过（任意渠道、排除节选小程序），且注册时间在12个小时以内（点击拼团时间为准）
        $checkType = PinTuan::TYPE_NEW;
        if ($groupJoinInfo['type'] == $checkType) {
            $newUser = self::checkNewPinTuanUser($uid);
            if (!$newUser) {
                throw new \Exception('抱歉，仅限新注册用户参团，你可以直接开团哦');
            }
        }
        return $groupJoinInfo;
    }

    private static function checkCreate($uid, $sku_id)
    {
        $nowDate = date('Y-m-d H:i:s');
        //查询团购价格
        $groupSku = PinTuanSku::get(['activity_price', 'id', 'pintuan_id', 'end_time'], [
            'sku_id' => $sku_id,
            'start_time[<]' => $nowDate,
            'end_time[>]' => $nowDate,
            'status' => PinTuan::STATUS_NORMAL,
        ]);

        if (!$groupSku) {
            throw new \Exception('查询团购价格信息不存在');
        }
        $group_buy_price = $groupSku['activity_price'];
        $group_id = $groupSku['pintuan_id'];
        $end_time = $groupSku['end_time'];

        //查询团活动信息
        $groupInfo = self::getGroupInfo($group_id, $nowDate);
        $type = $groupInfo['type'];
        $member = $groupInfo['group_member_num'];
        $shop_id = $groupInfo['shop_id'];
        $goods_id = $groupInfo['goods_id'];
        $group_cycle = $groupInfo['group_cycle'];

        //拉新团限制:用户与团购活动唯一性校验
        $checkType = PinTuan::TYPE_NEW;
        if ($type == $checkType) {
            $unique = GroupJoinMember::getJoinCount($uid, $group_id);
            if ($unique > 0) {
                throw new \Exception('抱歉，你正在参与当前团购哦');
            }

            $createCount = GroupJoin::getCreateCount($uid, $group_id);
            if ($createCount > 0) {
                throw new \Exception('此团你已经开启了，快去邀请别人成团吧');
            }
        }

        //查询目前创建团购数量
        $createCount = GroupJoin::getCreateCount($uid);
        if ($createCount >= self::CREATE_MAX) {
            throw new \Exception('抱歉，已到最大开团数哦');
        }

        return compact('group_buy_price', 'group_id', 'type', 'member', 'shop_id', 'goods_id', 'group_cycle', 'end_time');
    }
    /**
     * @desc 参团,返回ture表示成团
     * @param $uid
     * @param $group_join_id int 参团id
     * @param $sku_id
     * @param $order_no
     * @throws \Exception
     */
    private static function joinOne($uid, $group_join_id, $sku_id, $order_no)
    {
        try {
            if ($uid <= 0) {
                throw new \Exception('参团前必须登录');
            }

            //参团校验
            $groupJoinInfo = self::checkJoin($uid, $group_join_id);

            $group_id = $groupJoinInfo['group_id'];
            $type = $groupJoinInfo['type'];
            $shop_id = $groupJoinInfo['shop_id'];
            $goods_id = $groupJoinInfo['goods_id'];

            //添加团购成员信息
            $rs = GroupJoinMember::addData($group_join_id, $group_id, $type, $uid, $shop_id, $goods_id, $sku_id, $role = GroupJoinMember::ROLE_PARTNER, $order_no);
            if (!$rs) {
                throw new \Exception('添加团购成员信息失败');
            }
            self::infoLog('参团', ['request' => func_get_args(), 'response' => []]);
        } catch (\Exception $e) {
            $trace = [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'message' => $e->getMessage(),
            ];
            self::errorLog('参团', ['request' => func_get_args(), 'response' => compact('groupJoinInfo', 'rs', 'trace')]);
            throw $e;
        }
    }

    public static function cancelGroup()
    {
        //查询过期的团购
        $rs = GroupJoin::getCancelGroup();

        if ($rs) {
            self::infoLog('自动取消过期团购debug', compact('rs'));

            foreach ($rs as $value) {
                try {
                    $group_join_id = $value['id'];
                    $where = [
                        'group_join_id' => $group_join_id,
                        'status'        => [
                            GroupJoin::STATUS_ING
                        ],
                    ];
                    $info = GroupJoinMember::select_master(['status', 'order_no'], $where);

                    //团购信息失败处理
                    self::failGroup($group_join_id, GroupJoin::FAIL_REASON_OVER_TIME);

                    //收集退款订单号
                    $joinMemberOrderNo = [];
                    foreach ($info as $v) {
                        if ($v['status'] == GroupJoin::STATUS_ING) {
                            $joinMemberOrderNo[] = $v['order_no'];
                        }
                    }

                    //退款
                    self::_async($joinMemberOrderNo);

                    self::infoLog('自动取消过期团购', compact('group_join_id', 'info', 'joinMemberOrderNo'));
                } catch (\Exception $e) {
                    $file = $e->getFile();
                    $line = $e->getLine();
                    $message = $e->getMessage();
                    $trace = $e->getTrace();
                    $exception = compact('file', 'line', 'message', 'trace');
                    self::errorLog('自动取消过期团购', compact('group_join_id', 'info', 'joinMemberOrderNo', 'exception'));
                }
            }
        }
    }

    /**
     * @desc 成团失败
     * @param $group_join_id int 参团id
     * @param $reason int 失败原因
     * @throws \Exception
     */
    public static function failGroup($group_join_id, $reason = GroupJoin::FAIL_REASON_NOT_ENOUGH_STOCK)
    {
        GroupJoin::action(function() use ($group_join_id, $reason){
            $rs = GroupJoin::update(["status" => GroupJoin::STATUS_FAIL, "reason" => $reason], $condition = [
                'id' => $group_join_id,
                'status' => GroupJoin::STATUS_ING
            ])->rowCount();
            if (!$rs) {
                throw new \Exception('更新团购信息失败[成团失败]');
            }
            $rs = GroupJoinMember::update(["status" => GroupJoin::STATUS_FAIL], $condition = [
                'group_join_id' => $group_join_id,
                'status' => GroupJoin::STATUS_ING
            ])->rowCount();
            if (!$rs) {
                throw new \Exception('更新团成员信息失败[成团失败]');
            }
            self::infoLog('成团失败', ['request' => compact('group_join_id', 'reason')]);
        });
    }

    /**
     * @desc 团购业务错误日志
     * @param string $title 日志名称
     * @param array $params 存储参数
     * @return mixed
     */
    public static function errorLog($title, array $params = [])
    {
        tLog::warning('团购业务错误信息::' . $title, $params);
    }

    /**
     * @desc 信息日志
     * @param string $title 日志名称
     * @param array $params 存储参数
     * @return mixed
     */
    public static function infoLog($title, array $params = [])
    {
        tLog::info('团购业务信息记录::' . $title, $params);
    }

    public static function hideNick($nick)
    {
        $len = mb_strlen($nick);
        switch ($len) {
            case 1:
                break;
            case 2:
                $nick = mb_substr($nick, 0, 1) . '*';
                break;
            default:
                $nick = mb_substr($nick, 0, 1) . '**' . mb_substr($nick, -1, 1);
                break;
        }
        return $nick;
    }

    public static function getAliveByGroupId($groupId)
    {
        return PinTuan::count( [
            'id' => $groupId,
            'start_time[<]' => date('Y-m-d H:i:s'),
            'end_time[>]' => date('Y-m-d H:i:s'),
            'status' => PinTuan::STATUS_NORMAL,
        ]);
    }

    private static function getPinTuanIdByGoodsId($goodsId)
    {
        return PinTuan::get('id', [
            'goods_id' => $goodsId,
            'start_time[<]' => date('Y-m-d H:i:s'),
            'end_time[>]' => date('Y-m-d H:i:s'),
            'status' => PinTuan::STATUS_NORMAL,
        ]);
    }

    private static function getGroupInfo($group_id, $nowDate)
    {
        $groupInfo = PinTuan::get('*', [
            'id' => $group_id,
        ]);
        if (!$groupInfo) {
            throw new \Exception('团购活动不存在');
        } else {
            if ($groupInfo['status'] != PinTuan::STATUS_NORMAL) {
                throw new \Exception('团购活动已删除');
            }
            if ($nowDate < $groupInfo['start_time']) {
                throw new \Exception('拼团活动时间未开始');
            }
            if ($nowDate > $groupInfo['end_time']) {
                throw new \Exception('拼团活动时间已结束');
            }
        }
        return $groupInfo;
    }

    private static function formatInfo($info)
    {
        $response = [];
        if ($info) {
            foreach ($info as $value) {
                $value['sys_time'] = time();
                $value['begin_time'] = strtotime($value['start_time']);
                $value['end_time'] = strtotime($value['end_time']);
                $last_time = $value['end_time'] - $value['sys_time'];

                $value['time_down'] = [
                    'sys_time' => $value['sys_time'],
                    'begin_time' => $value['begin_time'],
                    'end_time' => $value['end_time'],
                ];

                $last_member_num = $value['member'] - $value['join_member'];
                $value['group_member_num'] = $value['member'];
                $value['last_time'] = $last_time > 0 ? $last_time : 0;
                $value['last_member_num'] = $last_member_num > 0 ? $last_member_num : 0;
                if ($value['status'] == GroupJoin::STATUS_ING && $value['last_time'] == 0) {
                    $value['reason'] = GroupJoin::FAIL_REASON_OVER_TIME;
                    $value['status'] = GroupJoin::STATUS_FAIL;
                }
                $response[] = $value;
            }
        }
        return $response;
    }

    public static $groupJoinInfoMap = [];
    public static function getGroupJoinInfo($group_join_id, $format = true)
    {
        $single = !is_array($group_join_id);
        $info = [];
        if ($single) {
            if (isset(self::$groupJoinInfoMap[$group_join_id])) {
                $info[] = self::$groupJoinInfoMap[$group_join_id];
            }
        } else {
            $multiple = [];
            foreach ($group_join_id as $key => $id) {
                if (isset(self::$groupJoinInfoMap[$id])) {
                    $multiple[] = self::$groupJoinInfoMap[$id];
                    unset($group_join_id[$key]);
                }
            }
        }

        if (empty($info) && !empty($group_join_id)) {
            //查询db
            $info = GroupJoin::select_master('*', ['id' => $group_join_id]);
            foreach ($info as $value) {
                if (!isset(self::$groupJoinInfoMap[$value['id']])) {
                    self::$groupJoinInfoMap[$value['id']] = $value;
                }
            }
        }

        if (!empty($multiple)) {
            $info = array_merge($info, $multiple);
        }

        if ($format) {
            $response = self::formatInfo($info);
            $response = $single && !empty($response) ? current($response) : $response;
        } else {
            $response = $info;
        }
        return $response;
    }

    public static function recommendGoods($uid = 0)
    {
        //推荐商品 最多推荐20个商品
        $maxCount = 20;

        //todo 拼团商品
        $where['AND'] = [
            'status' => PinTuan::STATUS_NORMAL,
            'start_time[<]' => date('Y-m-d H:i:s'),
            'end_time[>=]' => date('Y-m-d H:i:s'),
        ];
        $where['ORDER'] = ['end_time' => 'ASC'];
        $where['LIMIT'] = $maxCount;
        $pinTuanGoods = PinTuan::getList($where, ['goods_id', 'group_member_num']);
        $pinTuanGroupNum = empty($pinTuanGoods) ? [] : array_column($pinTuanGoods, 'group_member_num', 'goods_id');
        $pinTuanGoodsIds = empty($pinTuanGoods) ? [] : array_column($pinTuanGoods, 'goods_id');
        $lastCount  = $maxCount - count($pinTuanGoodsIds);
        $goodsIds = [];
        if ($lastCount > 0) {
            $newUser = self::checkNewPinTuanUser($uid);
            if ($newUser) {
                //新用户：优先拼团商品，活动结束时间正序（快结束的在上边）+新人专享商品（上架时间逆序）；
                $where = [
                    'end_time[>]' => date('Y-m-d H:i:s'),
                    'start_time[<]' => date('Y-m-d H:i:s'),
                    'status' => mNewCommerExclusive::STATUS_NORMAL,
                    'LIMIT' => $lastCount,
                    'ORDER' => [
                        'add_time' => 'DESC'
                    ],
                ];
                if ($pinTuanGoodsIds) {
                    $where['goods_id[!]'] = $pinTuanGoodsIds;
                }
                $list = mNewCommerExclusive::select(['goods_id'], $where);
                $goodsIds = empty($list) ? [] : array_column($list, 'goods_id');
            } else {
                //非新用户：拼团+热销商品（住友都在买）
                $recommendGoods = RecommendGoods::getRecommendGoodsInfo($uid, '', 1, [], [], $lastCount);
                $recommendGoods = isset($recommendGoods['goods']) ? $recommendGoods['goods'] : [];
                foreach ($recommendGoods as $v) {
                    $goodsId = isset($v['goods_info']['id']) ? $v['goods_info']['id'] : 0;
                    $goodsIds[] = $goodsId;
                }
            }
            //价格说明：根据商品参与的活动和活动优先级决定价格展示，若商品当前参与单个（或多个）活动，且最高的是拼团，则展示拼团标识（n人团）；拼团价和单买价
        }
        $recommend_goodsIds = array_merge($pinTuanGoodsIds, $goodsIds);

        //后端优先级说明：限时购 > 新人专享价 > 直降活动 > 拼团 >
        $recommend_goods = Goods::Goods($recommend_goodsIds);
        $actual_stock = Sku::Sku($recommend_goodsIds, Sku::FIELDS_GROUP_ALL, ['not_activity_type' => [ActivityInfo::ACTIVITY_TYPE_PINTUAN]]);
        $groupMap = sPinTuan::getCurrentList($recommend_goodsIds);
        $response = [];
        foreach ($recommend_goods as $goods_id => $v) {
            $checkStock = isset($pinTuanGroupNum[$goods_id]) ? $pinTuanGroupNum[$goods_id] - 1 : 0;
            $actual_stock_item = isset($actual_stock[$goods_id]) ? $actual_stock[$goods_id] : [];
            $sku_ids = isset($groupMap[$goods_id]['skus']) ? array_column($groupMap[$goods_id]['skus'], 'sku_id') : [];
            $v['activity_stock'] = isset($groupMap[$goods_id]['skus']) ? array_sum(array_column($groupMap[$goods_id]['skus'], 'actual_stock')): [];
            $v['actual_stock'] = 0;
            foreach ($sku_ids as $sku_id) {
                $v['actual_stock'] += isset($actual_stock_item['base']['goods_stock']) ? $actual_stock_item['base']['goods_stock'][$sku_id] : 0;
            }
            if (in_array($goods_id, $pinTuanGoodsIds)) {
                if (($v['actual_stock'] > $checkStock) && ($v['activity_stock'] > $checkStock)) {
                    if ($v['activity_type'] == ActivityInfo::ACTIVITY_TYPE_PINTUAN) {
                        $price = [];
                        foreach ($actual_stock[$goods_id]['sku_info'] as $skuInfo) {
                            if(in_array($skuInfo['sku_id'], $sku_ids)) {
                                $price[] = $skuInfo['ori_price'];
                            }
                        }
                        $v['ori_min_price'] = min($price);
                        $v['ori_max_price'] = max($price);
                        $v['actual_max_price'] = isset($groupMap[$goods_id]['max_price']) ? $groupMap[$goods_id]['max_price'] : $v['actual_max_price'];
                        $v['actual_min_price'] = isset($groupMap[$goods_id]['min_price']) ? $groupMap[$goods_id]['min_price'] : $v['actual_min_price'];
                    }
                    $response[] = $v;
                }
            } else {
                $response[] = $v;
            }
        }
        self::infoLog('推荐商品debug', compact('pinTuanGoodsIds', 'goodsIds', 'newUser', 'lastCount', 'uid', 'recommend_goodsIds'));
        return $response;
    }

    /**
     * 检查用户是否可以参加拉新团
     * @param $uid
     * @return bool
     */
    public static $newPinTuanUser = [];

    /**
     * @desc 查询店铺正在参与拼团的订单
     * @param $shopId string 订单编号
     * @param $start_time
     * @param $end_time
     * @param string $orderNo
     * @return array
     */
    public static function getShopJoinOrder($shopId, $start_time, $end_time, $orderNo = '')
    {
        $where = [
            'shop_id' => $shopId,
            'status' => GroupJoin::STATUS_ING,
        ];
        if ($start_time) {
            $where['start_time[>=]'] = $start_time;
        }
        if ($end_time) {
            $where['start_time[<]'] = $end_time;
        }
        if ($orderNo) {
            $where['order_no'] = $orderNo;
        }
        return GroupJoinMember::select('order_no', $where);
    }

    /**
     * @param $uid
     * @param bool $cache
     * @return bool|mixed
     */
    public static function checkNewPinTuanUser($uid, $cache = true)
    {
        if ($cache && isset(self::$newPinTuanUser[$uid])) {
            return self::$newPinTuanUser[$uid];
        }
        //新用户判断--没有支付过（任意渠道、排除节选小程序），且注册时间在12个小时以内
        $result = false;
        $now = time();
        $userInfo = User::getUser($uid);
        if ($userInfo) {
            $registerTime = $userInfo['addtime'];//用户注册时间
            $limitTime = 43200;//12小时
            if ($now >= $registerTime && ($now - $registerTime) <= $limitTime) {
                //查询用户有没有成功的团购
                $successGroupCount = GroupJoinMember::count([
                    'uid'       => $uid,
                    'status'    => GroupJoin::STATUS_SUCCESS,
                ]);
                if ($successGroupCount <= 0) {
                    $count = mOrder::count(['uid' => $uid, 'pay_no' => '', 'from_source[!]' => mOrder::FROM_SOURCE_PARAGRAPH_MINIPROGRAM]);
                    if (!$count) {
                        $result = true;
                    }
                }
            }
        }
        self::$newPinTuanUser[$uid] = $result;
        return $result;
    }

    public static function robot()
    {
        $nickArr = [
            '朝云暮雨',
            '昨夜西风',
            '孤女无爱',
            '浮名半生',
            '情窦初殇',
            '几度长歌',
            'Нaprn',
            'Dying',
            '猪猪侠',
            '坟黑猫 Roman',
            'Pretty boy',
            'Emotiona',
            '梁伟健',
            '寄生前夜',
            'manson',
            '灵魂武术',
            'Tony',
            '北冥有鱼',
            '端木清',
            '我是小明',
            'LOST',
            '小天狼星',
            '离我远点',
            'Bluelazio',
            '爱在西元前',
            '风中的永恒',
            '蓝色巨龙',
            '小小不为',
            '遗失的美好',
            '蜡笔小新',
            '卡卡啦',
            '蓝雪03',
            '顺其自然',
            '又有蝴蝶',
            '风语者',
            '执子之手',
            '风吹云动',
            '过去的日子',
            '环球旅行',
            '红牛小子',
            '飞度小妞',
            'Maya',
        ];
        $avatarArr = [
            'http://img.hhz1.cn/Op-imageShow/7f49f20b40b40dm00ppfe7b?iv=1',
            'http://img.hhz1.cn/Op-imageShow/2787f20b40b40ZY00ppfe7c?iv=1',
            'http://img.hhz1.cn/Op-imageShow/3105920b40b40hN00ppfe7c?iv=1',
            'http://img.hhz1.cn/Op-imageShow/a8808205k05k0ar00ppfe7c?iv=1',
            'http://img.hhz1.cn/Op-imageShow/bce3520b40b40je00ppfe7c?iv=1',
            'http://img.hhz1.cn/Op-imageShow/ec942205k05k0Dw00ppfe7c?iv=1',
            'http://img.hhz1.cn/Op-imageShow/05f6d205k05k0AL00ppfe7d?iv=1',
            'http://img.hhz1.cn/Op-imageShow/5f3aa20b40b403l00ppfe7d?iv=1',
            'http://img.hhz1.cn/Op-imageShow/09b4f20b40b40Ha00ppfe7d?iv=1',
            'http://img.hhz1.cn/Op-imageShow/26da2205k05k0WO00ppfe7e?iv=1',
            'http://img.hhz1.cn/Op-imageShow/745af20b40b404M00ppfe7e?iv=1',
            'http://img.hhz1.cn/Op-imageShow/3b5f820b40b40a500ppfe7e?iv=1',
            'http://img.hhz1.cn/Op-imageShow/f4c7520b40b40k400ppfe7e?iv=1',
            'http://img.hhz1.cn/Op-imageShow/f89f120b40b40l400ppfe7e?iv=1',
            'http://img.hhz1.cn/Op-imageShow/124f920b40b40vJ00ppfe7f?iv=1',
            'http://img.hhz1.cn/Op-imageShow/212ee20b40b40p600ppfe7f?iv=1',
            'http://img.hhz1.cn/Op-imageShow/76b1c205k05k0Cv00ppfe7f?iv=1',
            'http://img.hhz1.cn/Op-imageShow/40511208c08c0Cd00ppfe7f?iv=1',
            'http://img.hhz1.cn/Op-imageShow/334ea208c08c0pX00ppfe7g?iv=1',
            'http://img.hhz1.cn/Op-imageShow/f798a20b40b409a00ppfe7g?iv=1',
            'http://img.hhz1.cn/Op-imageShow/198dd20b40b40xr00ppfe7h?iv=1',
            'http://img.hhz1.cn/Op-imageShow/f5c60208c08c0ph00ppfe7h?iv=1',
            'http://img.hhz1.cn/Op-imageShow/0334c20b40b40NM00ppfe7h?iv=1',
            'http://img.hhz1.cn/Op-imageShow/16fac20b40b40Nb00ppfe7h?iv=1',
            'http://img.hhz1.cn/Op-imageShow/b2b0620b40b40P400ppfe7i?iv=1',
            'http://img.hhz1.cn/Op-imageShow/3c1e4205k05k0sS00ppfe7i?iv=1',
            'http://img.hhz1.cn/Op-imageShow/da05220b40b40wK00ppfe7i?iv=1',
            'http://img.hhz1.cn/Op-imageShow/dec4520b40b40BR00ppfe7i?iv=1',
            'http://img.hhz1.cn/Op-imageShow/27cf220b40b40dV00ppfe7i?iv=1',
            'http://img.hhz1.cn/Op-imageShow/5b66e20b40b40Pb00ppfe7j?iv=1',
            'http://img.hhz1.cn/Op-imageShow/1ce0920b40b40Z300ppfe7j?iv=1',
            'http://img.hhz1.cn/Op-imageShow/bf17420b40b40Xn00ppfe7j?iv=1',
            'http://img.hhz1.cn/Op-imageShow/9f5da20b40b40JP00ppfe7j?iv=1',
            'http://img.hhz1.cn/Op-imageShow/be86c205k05k0zN00ppfe7k?iv=1',
            'http://img.hhz1.cn/Op-imageShow/f83ce20b40b400N00ppfe7k?iv=1',
            'http://img.hhz1.cn/Op-imageShow/0899b20b40b401u00ppfe7k?iv=1',
            'http://img.hhz1.cn/Op-imageShow/8dd0120b40b40TL00ppfe7k?iv=1',
            'http://img.hhz1.cn/Op-imageShow/b29c320b40b409J00ppfe7l?iv=1',
            'http://img.hhz1.cn/Op-imageShow/eccb620b40b409D00ppfe7l?iv=1',
            'http://img.hhz1.cn/Op-imageShow/d5522205k05k0k400ppfe7l?iv=1',
            'http://img.hhz1.cn/Op-imageShow/939ca20b40b40xV00ppfe7l?iv=1',
            'http://img.hhz1.cn/Op-imageShow/7882b20b40b40Qy00ppfe7l?iv=1',
            'http://img.hhz1.cn/Op-imageShow/a999c205k05k0rc00ppfe7m?iv=1',
            'http://img.hhz1.cn/Op-imageShow/3e7b6208c08c0Lz00ppfe7m?iv=1',
            'http://img.hhz1.cn/Op-imageShow/7d451208c08c0lU00ppfe7m?iv=1',
            'http://img.hhz1.cn/Op-imageShow/0dbb820b40b40gA00ppfe7m?iv=1',
            'http://img.hhz1.cn/Op-imageShow/3bb71205k05k0XU00ppfe7m?iv=1',
            'http://img.hhz1.cn/Op-imageShow/db46e205k05k0M300ppfe7m?iv=1',
            'http://img.hhz1.cn/Op-imageShow/3c7ad20b40b40Gn00ppfe7n?iv=1',
            'http://img.hhz1.cn/Op-imageShow/cbdd420b40b40R100ppfe7n?iv=1',
            'http://img.hhz1.cn/Op-imageShow/d5fc820b40b400800ppfe7n?iv=1',
            'http://img.hhz1.cn/Op-imageShow/1ad8220b40b40Bh00ppfe7n?iv=1',
            'http://img.hhz1.cn/Op-imageShow/7115720b40b40uI00ppfe7n?iv=1',
            'http://img.hhz1.cn/Op-imageShow/41cf9205k05k0wR00ppfe7n?iv=1',
            'http://img.hhz1.cn/Op-imageShow/bb3b120b40b40SJ00ppfe7o?iv=1',
            'http://img.hhz1.cn/Op-imageShow/30a6220b40b40YB00ppfe7o?iv=1',
            'http://img.hhz1.cn/Op-imageShow/ff413205k05k0pS00ppfe7o?iv=1',
            'http://img.hhz1.cn/Op-imageShow/71e9f205k05k0qn00ppfe7o?iv=1',
            'http://img.hhz1.cn/Op-imageShow/34bc4208c08c0cq00ppfe7o?iv=1',
            'http://img.hhz1.cn/Op-imageShow/87af3205k05k0z700ppfe7o?iv=1',
            'http://img.hhz1.cn/Op-imageShow/a2d4720b40b40JY00ppfe7p?iv=1',
            'http://img.hhz1.cn/Op-imageShow/785f220b40b40vE00ppfe82?iv=1',
            'http://img.hhz1.cn/Op-imageShow/fbcbb205k05k0fb00ppfe83?iv=1',
            'http://img.hhz1.cn/Op-imageShow/f122f20b40b40hl00ppfe85?iv=1',
            'http://img.hhz1.cn/Op-imageShow/1edfc20b40b40vh00ppfe87?iv=1',
            'http://img.hhz1.cn/Op-imageShow/7e69a205k05k0Em00ppfe86?iv=1',
            'http://img.hhz1.cn/Op-imageShow/248f8208c08c08q00ppfe83?iv=1',
            'http://img.hhz1.cn/Op-imageShow/68f5820b40b40Kn00ppfe7r?iv=1',
            'http://img.hhz1.cn/Op-imageShow/76d3d20b40b40vo00ppfe7z?iv=1',
            'http://img.hhz1.cn/Op-imageShow/dee3f205k05k01s00ppfe7o?iv=1',
            'http://img.hhz1.cn/Op-imageShow/ba397205k05k0B300ppfe7o?iv=1',
            'http://img.hhz1.cn/Op-imageShow/538fb205k05k0tH00ppfe7n?iv=1',
            'http://img.hhz1.cn/Op-imageShow/73625205k05k0bZ00ppfe7m?iv=1',
            'http://img.hhz1.cn/Op-imageShow/8424520b40b408I00ppfe7k?iv=1',
            'http://img.hhz1.cn/Op-imageShow/9ff4f20b40b40el00ppfe7k?iv=1',
            'http://img.hhz1.cn/Op-imageShow/a2b6d205k05k0ks00ppfe7j?iv=1',
            'http://img.hhz1.cn/Op-imageShow/8d73620b40b40zt00ppfe7j?iv=1',
            'http://img.hhz1.cn/Op-imageShow/f91ed20b40b40jr00ppfe7j?iv=1',
            'http://img.hhz1.cn/Op-imageShow/9f5de20b40b40Om00ppfe7h?iv=1',
            'http://img.hhz1.cn/Op-imageShow/7616620b40b40wV00ppfe7g?iv=1',
            'http://img.hhz1.cn/Op-imageShow/62b5b208c08c0Ac00ppfe7g?iv=1',
            'http://img.hhz1.cn/Op-imageShow/5589120b40b40RV00ppfe7f?iv=1',
            'http://img.hhz1.cn/Op-imageShow/e9ffd20b40b403b00ppfe7e?iv=1',
        ];
        $nick = array_random($nickArr);
        $avatar = array_random($avatarArr);
        return compact('nick', 'avatar');
    }
}




